#include "vanila/vanila.h"
#include "vanila/virtualmachine.h"
#include "vanila/allocator.h"
#include "vanila/garbagecollector.h"
#include "vanila/compiler.h"
#include "vanila/parser.h"
#include "utils/singleton.h"
#include <fstream>
#include <sstream>
#include <iostream>

namespace vanila
{
const char* Vanila::label = 
" ____   ____    _        ____  _____   _____    _____           _\n"
"|_  _| |_  _|  / \\      |_   \\|_   _| |_   _|  |_   _|         / \\\n"   
"  \\ \\   / /   / _ \\       |   \\ | |     | |      | |          / _ \\\n"   
"   \\ \\ / /   / ___ \\      | |\\ \\| |     | |      | |   _     / ___ \\\n"  
"    \\ ' /  _/ /   \\ \\_   _| |_\\   |_   _| |_    _| |__/ |  _/ /   \\ \\_\n" 
"     \\_/  |____| |____| |_____|\\____| |_____|  |________| |____| |____|\n";

VirtualMachine* Vanila::vm = VirtualMachine::instance();

//! \brief release assert
void Vanila::release() noexcept
{
    Vanila::vm->release();

    // delete sigleton object
    utils::Singleton<VirtualMachine>::release();
    utils::Singleton<Allocator>::release();
    utils::Singleton<GarbageCollector>::release();
    utils::Singleton<Compiler>::release();
    utils::Singleton<Parser>::release();
}

//! \brief init assert
void Vanila::init()
{
    std::cout << Vanila::label << '\n';
    Vanila::vm->init();
    Vanila::vm->registerNativeFunctions();
    Vanila::vm->registerNativeClasses();
}

//! \brief interactive interface
void Vanila::interactive()
{
    bool multiple = false;
    for (;;)
    {
        std::string source;
        std::cout << ">>> ";

        for (;;)
        {
            std::string line;
            std::getline(std::cin, line);

            if (multiple == false)
            {
                if (line.back() == '\\')
                {
                    multiple = true;
                    source += line.substr(0, line.size() - 1);
                    source += '\n';
                    std::cout << "... ";
                }
                else
                {
                    source += line;
                    break;
                }
            }
            else
            {
                if (line.size() == 0)
                {
                    multiple = false;
                    break;
                }
                else
                {
                    source += line;
                    source += '\n';
                    std::cout << "... ";
                }
            }
        }

        if (source == "exit")
            break;

        // interpret source code
        Vanila::interpret(source);
    }
}

//! \brief run a script file
//! \param[in] path script file path
void Vanila::runFile(std::string path)
{
    // read file's content
    std::string source = Vanila::readFile(path);
    Vanila::interpret(source);
}

//! \brief read source code from script file
//! \param[in] path script file path
//! \return std::string
std::string Vanila::readFile(const std::string& path)
{
    std::ifstream file(path);
    std::stringstream buffer;
    buffer << file.rdbuf();
    return std::string(buffer.str());
}

//! \brief interpret the source code
//! \param[in] source
void Vanila::interpret(const std::string& source) noexcept
{
    try
    {
        Vanila::vm->interpret(source);
    } 
    catch (Error& error)
    {
        std::cout << error.what();
    }
}

}